/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package uk.ac.bath.schemes;

import uk.ac.bath.legacy.Gene;
import java.util.Arrays;
import uk.ac.bath.environment.MachineIF;
import uk.ac.bath.environment.Fitness;
import uk.ac.bath.environment.Scheme;
import uk.ac.bath.util.MyRandom;

/**
 *
 * @author pjl
 */
public class SaneScheme implements Scheme {

    SaneSubGene genes[];
    private SaneSubGene[] net_pop;
    int generation;
    int iTrial = 0;
    private SaneBuilder builder;
    int nPop;
    private int numBreed;
    private int trialsPerGeneration;

    public SaneScheme(int nPop, int nBreed, int trailsPerGeneration, BuilderIF builder) throws Exception {
        numBreed = nBreed;
        if (3 * nBreed > nPop) {
            throw new Exception(" Too many breeders max is" + nPop / 3);
        }
        this.trialsPerGeneration = trailsPerGeneration;
        this.builder = (SaneBuilder)builder;
        this.nPop = nPop;
        genes = new Gene[nPop];
    }

 
    public void init() {

        iTrial = 0;
        generation = 0;
 
        for (int i = 0; i < nPop; i++) {
            genes[i] = (SaneSubGene)builder.createSaneRandomSubGene();
        }
        net_pop = (SaneSubGene[])builder.createSaneGeneArray();
       
    }

    public MachineIF nextMachine() {

        /*find random subpopulation*/
        for (int j = 0; j < net_pop.length; ++j) {
            net_pop[j] = genes[MyRandom.nextInt(nPop)];
            net_pop[j].tests++;
        }

        MachineIF net = builder.build(net_pop);    /*form the network*/
        return net;
    }

    private void nextGeneration() {

        generation++;
        /*get average fitness level.  Multiply by 100 to get more resolution*/

        iTrial=0;
        
        for (int i = 0; i < nPop; ++i) {
            if (genes[i].tests > 0) {
                genes[i].fitness = (float) ((genes[i].fitness * 100.0) / genes[i].tests);
            } else {
                genes[i].fitness = 0;
            }
        }


        Arrays.sort(genes, SaneSubGene.rev);

        for (int i = 0; i < numBreed; i++) {
            SaneSubGene a = genes[i];
            int nn = i;
            if (i == 0) {
                nn = numBreed;
            }
            SaneSubGene b = genes[MyRandom.nextInt(nn)];



            SaneSubGene a1 = genes[nPop - 2 * i - 1];
            SaneSubGene b1 = genes[nPop - 2 * i - 2];

            builder.crossOver(a, b, a1, b1);

        }

        for (int i = numBreed; i < nPop; i++) {
            builder.mutate(genes[i]);


        }

        for (SaneSubGene g : genes) {
            g.fitness = 0.0f;
            g.tests = 0;
        }

    }

  

    @Override
    public void endOfEvaluation(Fitness fitness) {

        if (fitness != null) {
            for (int i = 0; i < net_pop.length; i++) {
                net_pop[i].fitness += fitness.fitness;
                net_pop[i].tests++;
            }

            if (++iTrial >= trialsPerGeneration) {
                nextGeneration();
            }
        }
     
    }

    public String getStatus() {
        return "SaneGeneration:"+generation+"  ";
    }

    public String reportSetup() {
        return "Scheme: Sane nPop="+nPop+" nBreed="+numBreed+" runsPerGeneration="+trialsPerGeneration +"\n"+
                builder.reportSetup();
    }
}


